import { useEffect, useRef, useState } from "react";
import "./index.less";

// const monthEn = [
//   "Jan",
//   "Feb",
//   "Mar",
//   "Apr",
//   "May",
//   "Jun",
//   "Jul",
//   "Aug",
//   "Sep",
//   "Oct",
//   "Nov",
//   "Dec",
// ];
const weekEn = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
interface CalenderConstruct {
  year: number;
  month: number;
  week: number;
  day: number;
  startPosition?: number[];
  endPosition?: number[];
}

interface MonthConstruct {
  year: number;
  month: number;
  startPosition?: number[];
  endPosition?: number[];
}

export default function Calender() {
  const setCalender = (date: Date): CalenderConstruct => {
    return {
      year: date.getFullYear(),
      month: date.getMonth(),
      week: date.getDay(),
      day: date.getDate(),
    };
  };
  const [calenderSize] = useState([700, 600]);

  //当前日期
  const [currentDay] = useState<CalenderConstruct>(() => {
    return setCalender(new Date());
  });

  //当前跳转的年月
  const [yearAndMonth, setYearAndMonth] = useState([0, 0]);

  //渲染到日历上的日期数组
  const [calenderList, setCalenderList] = useState<CalenderConstruct[] | null>(
    null
  );

  //渲染到日历上的月份数组
  const [monthList, setMonthList] = useState<MonthConstruct[] | null>(null);

  //当前hovering的日期
  const [hoveringCalenderCell, setHoveringCalenderCell] =
    useState<CalenderConstruct | null>(null);

  //当前hovering的月份
  const [hoveringMonthCell, setHoveringMonthCell] =
    useState<MonthConstruct | null>(null);

  const [month2YearSwitch, setMonth2YearSwitch] = useState(false);
  /**
   * canvasElement引用
   */
  const calenderCanvasRef = useRef<HTMLCanvasElement | null>(null);
  /**
   * 保存日历具体日期的canvas数据
   */
  const calenderImageDataRef = useRef<ImageData | null>(null);
  /**
   * 保存日历月份的canvas数据
   */
  const monthImageDataRef = useRef<ImageData | null>(null);
  /**
   * 开启高画质的canvas模式
   * @param canvas
   * @returns
   */
  const setupCanvas = (canvas: HTMLCanvasElement) => {
    // Get the device pixel ratio, falling back to 1.
    var dpr = window.devicePixelRatio || 1;
    // Get the size of the canvas in CSS pixels.
    var rect = canvas.getBoundingClientRect();
    // Give the canvas pixel dimensions of their CSS
    // size * the device pixel ratio.
    canvas.width = rect.width * dpr;
    canvas.height = rect.height * dpr;
    var ctx = canvas.getContext("2d");
    if (ctx != null) {
      // Scale all drawing operations by the dpr, so you
      // don't have to worry about the difference.
      ctx.scale(dpr, dpr);
      ctx.translate(0.5, 0.5);
    }
    return ctx;
  };

  /**
   * 渲染单个日期单元格
   * @param ctx
   * @param cell
   * @param height
   * @param options 为每个单元格定制样式
   */
  const renderDateForCalenderCell = (
    ctx: CanvasRenderingContext2D,
    cell: CalenderConstruct,
    height: number,
    options?: Object
  ) => {
    if (cell.startPosition && cell.endPosition) {
      // debugger;
      let x = (cell.startPosition[0] + cell.endPosition[0]) / 2;
      let y = (cell.startPosition[1] + cell.endPosition[1]) / 2;
      ctx.textAlign = "center";
      ctx.font = `${height / 5}px bold serif`;
      ctx.fillStyle = "#000000";
      ctx.fillText(cell.day.toString(), x, y);
      ctx.font = `${height / 7}px bold serif`;
      ctx.fillStyle = "#aaaaaa";
      ctx.fillText("待办事项", x, y + height / 4);
    }
  };
  /**
   * 渲染单个月份单元格
   * @param ctx
   * @param cell
   * @param height
   */
  const renderMonthForCalenderCell = (
    ctx: CanvasRenderingContext2D,
    cell: MonthConstruct,
    height: number
  ) => {
    if (cell.startPosition && cell.endPosition) {
      let x = (cell.startPosition[0] + cell.endPosition[0]) / 2;
      let y = (cell.startPosition[1] + cell.endPosition[1]) / 2;
      ctx.textAlign = "center";
      ctx.font = `${height / 4}px bold serif`;
      ctx.fillStyle = "#000000";
      ctx.fillText((cell.month + 1).toString() + "月", x, y);
      ctx.font = `${height / 6}px bold serif`;
      ctx.fillStyle = "#aaaaaa";
      ctx.fillText("点击进入具体月份", x, y + height / 4);
    }
  };
  /**
   * 监听canvas的鼠标移动事件，并作相关处理
   * @param event 鼠标移动的事件
   */
  const handleMouseMove = (
    event: React.MouseEvent<HTMLCanvasElement, MouseEvent>
  ) => {
    if (month2YearSwitch) {
      const cellWidth = calenderSize[0] / 4;
      const cellHeight = calenderSize[1] / 4;
      const numberOfCell =
        Math.floor(event.nativeEvent.offsetY / cellHeight) * 4 +
        Math.ceil(event.nativeEvent.offsetX / cellWidth) -
        1;
      if (monthList && monthList[numberOfCell]) {
        if (hoveringCalenderCell !== null) {
          //判断hovering的单元格是否没有变化
          if (
            hoveringCalenderCell.year !== monthList[numberOfCell].year ||
            hoveringCalenderCell.month !== monthList[numberOfCell].month
          ) {
            setHoveringMonthCell({ ...monthList[numberOfCell] });
          }
        } else {
          //第一次hover
          setHoveringMonthCell({ ...monthList[numberOfCell] });
        }
      }
    } else {
      const cellWidth = calenderSize[0] / 7;
      const cellHeight = calenderSize[1] / 6;
      const numberOfCell =
        Math.floor(event.nativeEvent.offsetY / cellHeight) * 7 +
        Math.ceil(event.nativeEvent.offsetX / cellWidth) -
        1;
      if (calenderList && calenderList[numberOfCell]) {
        if (hoveringCalenderCell !== null) {
          //判断hovering的单元格是否没有变化
          if (
            hoveringCalenderCell.year !== calenderList[numberOfCell].year ||
            hoveringCalenderCell.month !== calenderList[numberOfCell].month ||
            hoveringCalenderCell.day !== calenderList[numberOfCell].day
          ) {
            setHoveringCalenderCell({ ...calenderList[numberOfCell] });
          }
        } else {
          //第一次hover
          setHoveringCalenderCell({ ...calenderList[numberOfCell] });
        }
      }
    }
  };
  const handleMouseLeave = () => {
    if (month2YearSwitch) {
      setHoveringMonthCell(null);
    } else {
      setHoveringCalenderCell(null);
    }
  };

  const handleCalenderClick = (e: number) => {
    setYearAndMonth((prevState) => {
      let year = prevState[0];
      let month = prevState[1];
      month += e;
      if (month < 0) {
        year--;
        month += 12;
      } else if (month > 11) {
        year++;
        month -= 12;
      }
      return [year, month];
    });
  };

  useEffect(() => {
    if (currentDay) {
      setYearAndMonth([currentDay.year, currentDay.month]);
    }
  }, [currentDay]);

  useEffect(() => {
    if (calenderCanvasRef.current != null) {
      /**
       * 渲染日历具体日期的外壳
       * @param canvas
       * @param ctx
       */
      const renderCalenderWrapper = (
        canvas: HTMLCanvasElement,
        ctx: CanvasRenderingContext2D
      ) => {
        const cellWidth = canvas.width / 7;
        const cellHeight = canvas.height / 6;
        ctx.fillStyle = "white";
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.strokeStyle = "#222";
        ctx.lineWidth = 2;
        ctx.rect(0, 0, canvas.width - 1, canvas.height - 1);
        ctx.stroke();
        ctx.lineWidth = 1;
        ctx.strokeStyle = "#aaa";
        for (let i = 1; i < 7; i++) {
          ctx.moveTo(cellWidth * i, 0);
          ctx.lineTo(cellWidth * i, calenderSize[1]);
          ctx.stroke();
        }
        for (let i = 1; i < 6; i++) {
          ctx.moveTo(0, cellHeight * i);
          ctx.lineTo(calenderSize[0], cellHeight * i);
          ctx.stroke();
        }
      };
      /**
       * 渲染日历月份的外壳
       * @param canvas
       * @param ctx
       */
      const renderMonthWrapper = (
        canvas: HTMLCanvasElement,
        ctx: CanvasRenderingContext2D
      ) => {
        const cellWidth = canvas.width / 4;
        const cellHeight = canvas.height / 4;
        ctx.fillStyle = "white";
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.strokeStyle = "#222";
        ctx.lineWidth = 2;
        ctx.rect(0, 0, canvas.width - 1, canvas.height - 1);
        ctx.stroke();
        ctx.lineWidth = 1;
        ctx.strokeStyle = "#aaa";
        for (let i = 1; i < 4; i++) {
          ctx.moveTo(cellWidth * i, 0);
          ctx.lineTo(cellWidth * i, calenderSize[1]);
          ctx.stroke();
        }
        for (let i = 1; i < 4; i++) {
          ctx.moveTo(0, cellHeight * i);
          ctx.lineTo(calenderSize[0], cellHeight * i);
          ctx.stroke();
        }
      };
      //构造日历的数据
      const renderCurrentDate = (
        width: number,
        height: number,
        year: number,
        month: number
      ): CalenderConstruct[] => {
        const DateList: CalenderConstruct[] = [];
        const date = new Date(year, month);

        let offsetDays = date.getDay();
        let cellWidth = width / 7;
        let cellHeight = height / 6;
        for (let i = 0; i < 42; i++) {
          let res = setCalender(
            new Date(
              new Date(year, month).getTime() +
                (i - offsetDays) * 24 * 60 * 60 * 1000
            )
          );
          res.startPosition = [
            (i % 7) * cellWidth,
            Math.floor(i / 7) * cellHeight,
          ];
          res.endPosition = [
            ((i % 7) + 1) * cellWidth,
            (Math.floor(i / 7) + 1) * cellHeight,
          ];

          DateList.push(res);
        }
        return DateList;
      };
      //构造月份的数据
      const renderCurrentMonth = (
        width: number,
        height: number,
        year: number
      ): MonthConstruct[] => {
        const monthList: MonthConstruct[] = [];

        let cellWidth = width / 4;
        let cellHeight = height / 4;
        for (let i = 0; i < 16; i++) {
          let months: MonthConstruct = {
            year: year + Math.floor(i / 12),
            month: i % 12,
            startPosition: [
              cellWidth * (i % 4),
              cellHeight * Math.floor(i / 4),
            ],
            endPosition: [
              cellWidth * (i % 4) + cellWidth,
              cellHeight * Math.floor(i / 4) + cellHeight,
            ],
          };
          monthList.push(months);
        }
        return monthList;
      };

      //默认显示的当前日期所在的月份及其周围的总数为42天的数据
      if (yearAndMonth[0] !== 0 || yearAndMonth[1] !== 0) {
        if (month2YearSwitch) {
          setMonthList(() => {
            return renderCurrentMonth(
              calenderSize[0],
              calenderSize[1],
              yearAndMonth[0]
            );
          });
          const ctx = setupCanvas(calenderCanvasRef.current);
          if (ctx != null) {
            renderMonthWrapper(calenderCanvasRef.current, ctx);
            monthImageDataRef.current = ctx.getImageData(
              0,
              0,
              calenderSize[0],
              calenderSize[1]
            );
          }
        } else {
          setCalenderList(() => {
            return renderCurrentDate(
              calenderSize[0],
              calenderSize[1],
              yearAndMonth[0],
              yearAndMonth[1]
            );
          });
          const ctx = setupCanvas(calenderCanvasRef.current);
          if (ctx != null) {
            renderCalenderWrapper(calenderCanvasRef.current, ctx);
            calenderImageDataRef.current = ctx.getImageData(
              0,
              0,
              calenderSize[0],
              calenderSize[1]
            );
          }
        }
      }
    }
  }, [yearAndMonth, month2YearSwitch, calenderSize]);

  useEffect(() => {
    /**
     * 渲染日历所有的单元
     * @param canvas
     * @param ctx
     * @param calenderList
     */
    const renderCalenderAllCell = (
      canvas: HTMLCanvasElement,
      ctx: CanvasRenderingContext2D,
      calenderList: CalenderConstruct[]
    ) => {
      const cellHeight = canvas.height / 6;
      ctx.translate(0.5, 0.5);

      if (calenderList) {
        calenderList.forEach((date) => {
          renderDateForCalenderCell(ctx, date, cellHeight);
        });
      }
    };
    /**
     * 标记当前日期的位置
     */
    const markCurrentDatePosition = (
      ctx: CanvasRenderingContext2D,
      currentDate: CalenderConstruct,
      calenderList: CalenderConstruct[]
    ) => {
      const cell = calenderList.find((date) => {
        return (
          date.day === currentDate.day &&
          date.month === currentDate.month &&
          date.year === currentDate.year
        );
      });
      if (cell && cell.startPosition && cell.endPosition) {
        ctx.fillStyle = "#01afaf";
        ctx.globalAlpha = 0.3;
        ctx.fillRect(
          cell.startPosition[0],
          cell.startPosition[1],
          cell.endPosition[0] - cell.startPosition[0],
          cell.endPosition[1] - cell.startPosition[1]
        );
      }
    };
    /**
     * 渲染月份的边界
     * @param ctx
     * @param calenderList
     * @param currentMonth
     * @param currentYear
     */
    const showMonthBoundary = (
      ctx: CanvasRenderingContext2D,
      calenderList: CalenderConstruct[]
    ) => {
      if (calenderList) {
        const months = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
        calenderList.forEach((date) => {
          months[date.month]++;
        });
        let currentMonth = 0;
        let max = months[0];
        months.forEach((month, i) => {
          if (max < month) {
            max = month;
            currentMonth = i;
          }
        });
        const dateByMonth = calenderList.filter((date) => {
          return date.month === currentMonth;
        });
        const path: number[][] = [];
        let theLastDayOfMonth = dateByMonth[dateByMonth.length - 1];
        //绘制当前月份的边界路线
        if (
          dateByMonth &&
          dateByMonth.length > 2 &&
          dateByMonth[0].startPosition &&
          dateByMonth[0].endPosition &&
          theLastDayOfMonth.startPosition &&
          theLastDayOfMonth.endPosition
        ) {
          path.push(dateByMonth[0].startPosition);
          path.push([calenderSize[0] - 1, dateByMonth[0].startPosition[1]]);
          path.push([calenderSize[0] - 1, theLastDayOfMonth.startPosition[1]]);
          path.push([
            theLastDayOfMonth.endPosition[0],
            theLastDayOfMonth.startPosition[1],
          ]);
          path.push(theLastDayOfMonth.endPosition);
          path.push([0, theLastDayOfMonth.endPosition[1]]);
          path.push([0, dateByMonth[0].endPosition[1]]);
          path.push([
            dateByMonth[0].startPosition[0],
            dateByMonth[0].endPosition[1],
          ]);
          path.push(dateByMonth[0].startPosition);
        }

        //渲染边界
        ctx.beginPath();
        ctx.strokeStyle = "#01afaf";
        ctx.globalAlpha = 0.8;
        ctx.lineWidth = 3;
        path.forEach((point, index) => {
          if (index === 0) {
            ctx.moveTo(point[0], point[1]);
          } else {
            ctx.lineTo(point[0], point[1]);
          }
        });
        ctx.stroke();
      }
    };
    if (calenderCanvasRef.current != null) {
      const ctx = calenderCanvasRef.current.getContext("2d");
      if (ctx != null && calenderList) {
        renderCalenderAllCell(calenderCanvasRef.current, ctx, calenderList);
        if (currentDay) {
          markCurrentDatePosition(ctx, currentDay, calenderList);
        }
        showMonthBoundary(ctx, calenderList);
        calenderImageDataRef.current = ctx.getImageData(
          0,
          0,
          calenderSize[0],
          calenderSize[1]
        );
      }
    }
  }, [currentDay, calenderList, calenderSize]);

  useEffect(() => {
    /**
     * 渲染日历所有的单元
     * @param canvas
     * @param ctx
     * @param monthList
     */
    const renderCalenderAllCell = (
      canvas: HTMLCanvasElement,
      ctx: CanvasRenderingContext2D,
      monthList: MonthConstruct[]
    ) => {
      const cellHeight = canvas.height / 6;
      ctx.translate(0.5, 0.5);

      if (monthList) {
        monthList.forEach((month) => {
          renderMonthForCalenderCell(ctx, month, cellHeight);
        });
      }
    };

    /**
     * 标记当前日期的位置
     */
    const markCurrentDatePosition = (
      ctx: CanvasRenderingContext2D,
      currentDate: CalenderConstruct,
      calenderList: MonthConstruct[]
    ) => {
      const cell = calenderList.find((date) => {
        return (
          date.month === currentDate.month && date.year === currentDate.year
        );
      });
      if (cell && cell.startPosition && cell.endPosition) {
        ctx.fillStyle = "#01afaf";
        ctx.globalAlpha = 0.3;
        ctx.fillRect(
          cell.startPosition[0],
          cell.startPosition[1],
          cell.endPosition[0] - cell.startPosition[0],
          cell.endPosition[1] - cell.startPosition[1]
        );
      }
    };
    if (calenderCanvasRef.current != null) {
      const ctx = calenderCanvasRef.current.getContext("2d");
      if (ctx != null && monthList) {
        renderCalenderAllCell(calenderCanvasRef.current, ctx, monthList);
        if (currentDay) {
          markCurrentDatePosition(ctx, currentDay, monthList);
        }
        monthImageDataRef.current = ctx.getImageData(
          0,
          0,
          calenderSize[0],
          calenderSize[1]
        );
      }
    }
  }, [currentDay, monthList, calenderSize]);

  useEffect(() => {
    const markDatePosition = (calenderCell: CalenderConstruct | null) => {
      setTimeout(() => {
        if (calenderCell != null) {
          if (calenderCanvasRef.current != null) {
            const ctx = calenderCanvasRef.current.getContext("2d");
            if (ctx != null && calenderCell.startPosition) {
              ctx.putImageData(calenderImageDataRef.current as ImageData, 0, 0);

              ctx.shadowColor = "#01afaf";
              ctx.shadowBlur = 10;
              ctx.fillStyle = "white";
              ctx.globalAlpha = 1;
              ctx.fillRect(
                calenderCell.startPosition[0],
                calenderCell.startPosition[1],
                calenderSize[0] / 7,
                calenderSize[1] / 6
              );
              ctx.shadowColor = "white";
              renderDateForCalenderCell(ctx, calenderCell, calenderSize[1] / 6);
            }
          }
        } else {
          if (calenderCanvasRef.current != null) {
            const ctx = calenderCanvasRef.current.getContext("2d");
            if (ctx != null) {
              ctx.putImageData(calenderImageDataRef.current as ImageData, 0, 0);
            }
          }
        }
      }, 0);
    };

    markDatePosition(hoveringCalenderCell);
  }, [hoveringCalenderCell, calenderSize]);

  useEffect(() => {
    const markDatePosition = (monthCell: MonthConstruct | null) => {
      setTimeout(() => {
        if (monthCell != null) {
          if (calenderCanvasRef.current != null) {
            const ctx = calenderCanvasRef.current.getContext("2d");
            if (ctx != null && monthCell.startPosition) {
              ctx.putImageData(monthImageDataRef.current as ImageData, 0, 0);

              ctx.shadowColor = "#01afaf";
              ctx.shadowBlur = 10;
              ctx.fillStyle = "white";
              ctx.globalAlpha = 1;
              ctx.fillRect(
                monthCell.startPosition[0],
                monthCell.startPosition[1],
                calenderSize[0] / 4,
                calenderSize[1] / 4
              );
              ctx.shadowColor = "white";
              renderMonthForCalenderCell(ctx, monthCell, calenderSize[1] / 4);
            }
          }
        } else {
          if (calenderCanvasRef.current != null) {
            const ctx = calenderCanvasRef.current.getContext("2d");
            if (ctx != null) {
              ctx.putImageData(monthImageDataRef.current as ImageData, 0, 0);
            }
          }
        }
      }, 0);
    };
    if (hoveringMonthCell) {
      markDatePosition(hoveringMonthCell);
    }
  }, [hoveringMonthCell, calenderSize]);

  useEffect(() => {
    if (month2YearSwitch) {
      if (calenderCanvasRef.current) {
        const ctx = calenderCanvasRef.current.getContext("2d");
        if (ctx) {
          setTimeout(() => {
            if (monthImageDataRef.current) {
              ctx.putImageData(monthImageDataRef.current, 0, 0);
            }
          }, 0);
        }
      }
    } else {
      if (calenderCanvasRef.current) {
        const ctx = calenderCanvasRef.current.getContext("2d");
        if (ctx) {
          setTimeout(() => {
            if (calenderImageDataRef.current) {
              ctx.putImageData(calenderImageDataRef.current, 0, 0);
            }
          }, 0);
        }
      }
    }
  }, [month2YearSwitch, calenderSize]);
  return (
    <div id="calender-wrapper">
      <div
        className="calender-header"
        style={{ width: `${calenderSize[0]}px` }}
      >
        <div className="calender-header-tool">
          {month2YearSwitch ? (
            <div
              className="calender-header-year"
              onClick={() => {
                setMonth2YearSwitch(false);
              }}
            >
              {yearAndMonth ? <span>{yearAndMonth[0]}</span> : <></>}
            </div>
          ) : (
            <div
              className="calender-header-year-and-month"
              onClick={() => {
                setMonth2YearSwitch(true);
              }}
            >
              {yearAndMonth ? (
                <>
                  <span>{yearAndMonth[0]}</span> <span>{"/"}</span>
                  <span>{yearAndMonth[1] + 1}</span>
                </>
              ) : (
                <></>
              )}
            </div>
          )}
          <div className="calender-header-month-switch">
            <span
              className="button"
              onClick={() => {
                handleCalenderClick(-1);
              }}
            >
              {"<"}
            </span>
            <span
              className="button"
              onClick={() => {
                handleCalenderClick(1);
              }}
            >
              {">"}
            </span>
          </div>
        </div>
        <div
          className={`calender-header-title ${
            month2YearSwitch === true ? "hide" : ""
          }`}
        >
          {weekEn.map((week, index) => {
            return (
              <div className="calender-header-cell" key={index}>
                <span className="calender-header-cell-title">{week}</span>
              </div>
            );
          })}
        </div>
      </div>
      <canvas
        id="calender-content"
        ref={calenderCanvasRef}
        width={calenderSize[0]}
        height={calenderSize[1]}
        onMouseMove={(event) => {
          handleMouseMove(event);
        }}
        onMouseLeave={handleMouseLeave}
      ></canvas>
    </div>
  );
}
